import asyncio
from pathlib import Path

from aiogram import Bot, Dispatcher
from aiogram.fsm.storage.memory import MemoryStorage, SimpleEventIsolation
from aiogram.fsm.storage.redis import DefaultKeyBuilder, RedisStorage
from aiogram_dialog import DialogRegistry
from apscheduler.schedulers.asyncio import AsyncIOScheduler

from app.bot.handlers import register_dialogs, register_handlers
from app.bot.middlewares.config import ConfigMiddleware
from app.bot.middlewares.db import DBSessionMiddleware
from app.bot.middlewares.locale import LocaleMiddleware
from app.bot.middlewares.paypalych import PaymentMiddleware
from app.bot.middlewares.scheduler import SchedulerMiddleware
from app.bot.services.broadcaster import broadcast
from app.bot.services.locale import LocaleLoader, Localizator
from app.bot.services.loggers import configure_logging, logger
from app.bot.services.paypalych_api.client import PayPalychClient
from app.bot.services.scheduler import jobs, setup_scheduler
from app.bot.services.set_commands import set_commands
from app.config import Settings, load_config
from app.database.db import sa_sessionmaker

BOTFOLDER = Path(__file__).parent


def setup_middlewares(
    dp: Dispatcher,
    session_factory,
    config: Settings,
    fluent,
    scheduler: AsyncIOScheduler,
    payment_client: PayPalychClient,
):
    dp.message.outer_middleware(DBSessionMiddleware(session_factory))
    dp.callback_query.outer_middleware(DBSessionMiddleware(session_factory))
    dp.update.outer_middleware(DBSessionMiddleware(session_factory))
    dp.message.outer_middleware.register(ConfigMiddleware(config))
    dp.callback_query.outer_middleware.register(ConfigMiddleware(config))
    dp.message.outer_middleware.register(LocaleMiddleware(localizator=fluent))
    dp.callback_query.outer_middleware.register(LocaleMiddleware(localizator=fluent))
    dp.message.outer_middleware.register(SchedulerMiddleware(scheduler))
    dp.callback_query.outer_middleware.register(SchedulerMiddleware(scheduler))
    dp.message.outer_middleware.register(PaymentMiddleware(payment_client))
    dp.callback_query.outer_middleware.register(PaymentMiddleware(payment_client))


def _configure_fluent(locales_path):
    locales_map = {
        "ru": ("ru",),
        "en": ("en",),
    }
    loader = LocaleLoader(
        Path(locales_path),
    )
    return Localizator(loader, locales_map)


async def main():

    config = load_config()
    configure_logging()

    logger.error("Starting bot")
    if config.tg_bot.use_redis:
        storage = RedisStorage.from_url(
            url=config.redis.url,
            key_builder=DefaultKeyBuilder(with_destiny=True),
        )
    else:
        storage = MemoryStorage()

    bot = Bot(token=config.tg_bot.token, parse_mode="HTML")
    dp = Dispatcher(storage=storage, events_isolation=SimpleEventIsolation())
    registry = DialogRegistry(dp)
    session_factory = await sa_sessionmaker(config.db, echo=False)
    fluent = _configure_fluent(BOTFOLDER / Path("locales/"))
    payment_client = PayPalychClient(
        api_token=config.api.token, shop_id=config.api.shop_id
    )
    scheduler = setup_scheduler()
    scheduler.add_job(
        jobs.autorenew_orders,
        "interval",
        minutes=7,
        name="Autorenew orders",
        args=(bot, session_factory, config, fluent),
    )
    scheduler.add_job(
        jobs.archiving_expired_orders,
        "interval",
        minutes=10,
        name="Archiving expired orders",
        args=(bot, session_factory, config, fluent),
    )
    scheduler.add_job(
        jobs.notify_expiring_orders,
        "interval",
        minutes=5,
        name="Notifying expiring orders",
        args=(bot, session_factory, config, fluent),
    )
    scheduler.add_job(
        jobs.check_proxies,
        "interval",
        minutes=6,
        name="Checking all proxies",
        args=(bot, session_factory, config),
    )
    logger.info("Запущен планировщик задач")
    scheduler.start()

    setup_middlewares(dp, session_factory, config, fluent, scheduler, payment_client)

    register_handlers(dp)
    register_dialogs(registry)

    await set_commands(bot)
    await broadcast(bot, config.tg_bot.admin_ids, "Бот запущен!")
    await dp.start_polling(bot)


def cli():
    """Wrapper for command line, app's entry point"""
    try:
        asyncio.run(main())
    except (KeyboardInterrupt, SystemExit):
        logger.error("Bot stopped!")


if __name__ == "__main__":
    cli()
